//*****************************************************************************
//
// EE 345M - Valvano
// Spring 2011
// Created by: Paul Kreter
// TA: Raffaele Cetrulo TTh 6:30 - 8:00
//
// interpreter.c - Headers for using the Interpreter functions.
// Interpreter functions will handle user input from the
// UART port via HyperTerminal.
//
//*****************************************************************************
#include <string.h>
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_sysctl.h"
#include "inc/hw_types.h"
#include "inc/hw_uart.h"
#include "driverlib/debug.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/uart.h"
#include "utils/cmdline.h"
#include "adc.h"
#include "interpreter.h"
#include "efile.h"
#include "tachometer.h"

//*****************************************************************************
//
// Software FIFO skeleton.
// COPIED FROM DR. VALVANO'S LAB 1 DESCRIPTION
//
//*****************************************************************************
#define AddFifo(NAME,SIZE,TYPE, SUCCESS,FAIL) \
unsigned long volatile PutI ## NAME; \
unsigned long volatile GetI ## NAME; \
TYPE static Fifo ## NAME [SIZE]; \
void NAME ## Fifo_Init(void){ \
PutI ## NAME= GetI ## NAME = 0; \
} \
int NAME ## Fifo_Put (TYPE data){ \
if(( PutI ## NAME - GetI ## NAME ) & ~(SIZE-1)){ \
return(FAIL); \
} \
Fifo ## NAME[ PutI ## NAME &(SIZE-1)] = data; \
PutI ## NAME ## ++; \
return(SUCCESS); \
} \
int NAME ## Fifo_Get (TYPE *datapt){ \
if( PutI ## NAME == GetI ## NAME ){ \
return(FAIL); \
} \
*datapt = Fifo ## NAME[ GetI ## NAME &(SIZE-1)];\
GetI ## NAME ## ++; \
return(SUCCESS); \
}

//*****************************************************************************
//
// The maximum size of a line to the UART interface
//
//*****************************************************************************
#define CMD_SIZE 64

//*****************************************************************************
//
// Transmit and Receive software FIFOs
//
//*****************************************************************************
AddFifo(Tx,CMD_SIZE,char, 1,0)
AddFifo(Rx,CMD_SIZE,char, 1,0)

//*****************************************************************************
//
// Semaphores:
// RxChars - number of characters in RxFIFO (counter semaphore)
// RxInterpret - 0 = interpreter idle, 1 = command received (binary semaphore)
//
//*****************************************************************************
unsigned short volatile RxChars = 0;
unsigned short volatile TxChars = 0;

//*****************************************************************************
//
// Prototypes for command handlers.
// 
// Command handlers and table derived from StellarisWare example project
// 		using the cmdline utility provided by Texas Instruments.
//
//*****************************************************************************
extern int Cmd_help(int argc, char *argv[]);
extern int Cmd_format(int argc, char *argv[]);
extern int Cmd_dir(int argc, char *argv[]);
extern int Cmd_print(int argc, char *argv[]);
extern int Cmd_delete(int argc, char *argv[]);
extern int Cmd_write(int argc, char *argv[]);
extern int Cmd_new(int argc, char *argv[]);
extern int Cmd_tach(int argc, char *argv[]);

//*****************************************************************************
//
// This is the table that holds the command names, implementing functions,
// and brief description.
//
//*****************************************************************************
tCmdLineEntry g_sCmdTable[] =
{
    { "help",       Cmd_help,   " : Display list of commands" },
    { "h",          Cmd_help,   " : alias for help" },
    { "?",          Cmd_help,   " : alias for help" },
    { "format",     Cmd_format, " : Formats the SD card" },
    { "directory",  Cmd_dir,    " : Prints the file directory" },
	{ "new",	    Cmd_new,	" : Creates a new file"},
	{ "write",	    Cmd_write,	" : Write data to a file" },
    { "print",      Cmd_print,  " : Prints the last block of a designated file" },
	{ "delete",	    Cmd_delete, " : Deletes a designated file" },
	{ "tachometer",	Cmd_tach,	" : Prints the tachometer period" },
	{ "t",			Cmd_tach,	" : alias for tachometer" },
    { 0, 0, 0 }
};

//*****************************************************************************
//
// ORIGINAL FUNCTION WRITTEN BY PAUL KRETER
//
// Send a string to the UART.
//
//*****************************************************************************
int
UARTSend(const char *pucBuffer)
{

	//
	// Loop while there are more characters to send.
	//
	while(*pucBuffer)
	{
		//
		// Write the next character to the UART.
		//
		UARTCharPut(UART0_BASE, *pucBuffer++);
	}
	return 0;
}

//*****************************************************************************
//
// ORIGINAL FUNCTION WRITTEN BY PAUL KRETER
//
// Send a long to the UART.
//
//*****************************************************************************
int
UARTSendLong(unsigned long data)
{
	char print[11];
	char parse[10];
	int count = 0;
	int place = 0;
	do{
		parse[count++] = '0' + data%10;
		data = data/10;
	}while(data!=0);

	while(count>0)
	{
		print[place++] = parse[--count];
	}
	print[place] = 0x00;
	return UARTSend(&print[0]);
}

//*****************************************************************************
//
// ORIGINAL FUNCTION WRITTEN BY PAUL KRETER
//
// Initializes GPIO port A as UART
// Configure the UART for 115,200, 8-N-1 operation
//
//*****************************************************************************
void
InterpreterInit(void)
{
	//
	// Set GPIO A0 and A1 as UART pins.
	//
	GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

	//
	// Configure the UART for 115,200, 8-N-1 operation.
	//
	UARTConfigSetExpClk(UART0_BASE, SysCtlClockGet(), 115200,
		(UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
	
	// Initialize the Rx and Tx FIFOs
	RxFifo_Init();
	TxFifo_Init();
	
	//
	// Enable the UART interrupt.
	//
	IntEnable(INT_UART0);
	UARTIntEnable(UART0_BASE, UART_INT_TX | UART_INT_RX | UART_INT_RT);
	
	// Serial Welcome
	UARTSend("\n\r");
	UARTSend("***********************************\n\r");
	UARTSend("***        EE 345M Lab1        ****\n\r");
	UARTSend("***   Matt Gee, Paul Kreter    ****\n\r");
	UARTSend("***  Command Line Interpreter  ****\n\r");
	UARTSend("***********************************\n\r");
	UARTSend("Type \'help\' for help.\n\r");
	UARTSend("> ");
}

//*****************************************************************************
//
// Interpret user commands
//
// Adapted from StellarisWare cmdline example.
//
//*****************************************************************************
void
InterpretCommands(void)
{
	char command[CMD_SIZE];
	int count = 0;
	int status;
	while(RxChars)
	{
		RxFifo_Get(&command[count]);
		count++;
		RxChars--;
	}
	RxFifo_Get(&command[count]);
	
	//
	// Pass the command to the command processor
	//
	status = CmdLineProcess(command);
		
	//
	// Handles invalid commands
	//
	if(status == CMDLINE_BAD_CMD)
	{
		UARTSend("Invalid command\n\r");
	}
		
	//
	// Handles the case of too many arguments.
	//
	else if(status == CMDLINE_TOO_MANY_ARGS)
	{
		UARTSend("Too many arguments\n\r");
	}
	
	//
	// Print a prompt on the console.
	//
	UARTSend("> ");
}

//*****************************************************************************
//
// ORIGINAL FUNCTION WRITTEN BY PAUL KRETER
//
// The UART interrupt handler.
//
//*****************************************************************************
void
UARTIntHandler(void)
{
	unsigned long ulStatus;
	char next;
	
	//
	// Get the interrrupt status.
	//
	ulStatus = UARTIntStatus(UART0_BASE, true);
	
	//
	// Clear the asserted interrupts.
	//
	UARTIntClear(UART0_BASE, ulStatus);
	
	//
	// Loop while there are characters in the receive FIFO.
	//
	while(UARTCharsAvail(UART0_BASE))
	{
		//
		// Read the next character from the UART and write it back to the UART.
		//
		next = UARTCharGet(UART0_BASE);
		if(next == 0x0D)
		{
			UARTSend("\n\r");
			RxFifo_Put(0x00);
			RxChars++;
			InterpretCommands();
		}
		else
		{
			RxFifo_Put(next);
			UARTSend(&next);
			RxChars++;
		}
	}
	//
	// Loop while there are characters in the transmit FIFO
	//
	while(TxChars>0)
	{
		TxFifo_Get(&next);
		UARTCharPut(UART0_BASE, next);
		TxChars--;
	}
}

//*****************************************************************************
//
// This function implements the "help" command. It displays all the supported
// commands and provides a brief description of each.
//
// Adapted from StellarisWare cmdline example.
//
//*****************************************************************************
int Cmd_help(int argc, char *argv[])
{
	tCmdLineEntry *pEntry;
	
	//
	// Print some header text.
	//
	UARTSend("\nAvailable commands\n\r");
	UARTSend("------------------\n\r");
	
	//
	// Point at the beginning of the command table.
	//
	pEntry = &g_sCmdTable[0];
	
	//
	// Enter a loop to read each entry from the command table. The
	// end of the table has been reached when the command name is NULL.
	//
	while(pEntry->pcCmd)
	{
		//
		// Print the command name and the brief description.
		//
		UARTSend(pEntry->pcCmd);
		UARTSend(pEntry->pcHelp);
		UARTSend("\n\r");
		
		//
		// Advance to the next entry in the table.
		//
		pEntry++;
	}
	UARTSend("\n\r");
	
	//
	// Return success.
	//
	return 0;
}

//*****************************************************************************
//
// ORIGINAL FUNCTION WRITTEN BY PAUL KRETER
//
// This function implements the "format" command.
//
//*****************************************************************************
int Cmd_format(int argc, char *argv[])
{
	eFile_Format();

	return 0;
}

//*****************************************************************************
//
// ORIGINAL FUNCTION WRITTEN BY PAUL KRETER
//
// This function implements the "directory" command.
//
//*****************************************************************************
int Cmd_dir(int argc, char *argv[])
{
	//Need to integrate this command better
	//eFile_Directory(*UARTSend);
	UARTSend("Currently non functional\n\r");
	return 0;
}

//*****************************************************************************
//
// ORIGINAL FUNCTION WRITTEN BY PAUL KRETER
// ARGUMENT PARSING ADAPTED FROM CMDLINE EXAMPLE
//
// This function implements the "print" command.
// 
//*****************************************************************************
int Cmd_print(int argc, char *argv[])
{
	//int count;
	//char file[7];
	//char data[508];

	UARTSend("Currently non functional\n\r");
	
	/*if(argc != 2)
	{
		UARTSend("This function requires a file name argument\n\r");
		return -3;
	}

	for(count = 0; argv[1][count] != 0; count++)
		file[count] = argv[1][count];
	for(; count<7; count++)
		file[count] = ' ';

	if(eFile_ROpen(file))
	{
		UARTSend("Failure: No such file\n\r");
		return 1;
	}
	else
	{
		while(!eFile_ReadNext(data))
			UARTSend(data);
		UARTSend("\n\r");
	}*/

	return 0;
}

//*****************************************************************************
//
// ORIGINAL FUNCTION WRITTEN BY PAUL KRETER
// ARGUMENT PARSING ADAPTED FROM CMDLINE EXAMPLE
//
// This function implements the "delete" command.
// 
//*****************************************************************************
int Cmd_delete(int argc, char *argv[])
{
	int count;
	char file[7];

	if(argc != 2)
	{
		UARTSend("This function requires a file name argument\n\r");
		return -3;
	}

	for(count = 0; argv[1][count] != 0; count++)
		file[count] = argv[1][count];
	for(; count<7; count++)
		file[count] = ' ';
				  
	eFile_Delete(file);

	return 0;
}

//*****************************************************************************
//
// ORIGINAL FUNCTION WRITTEN BY PAUL KRETER
// ARGUMENT PARSING ADAPTED FROM CMDLINE EXAMPLE
//
// This function implements the "write" command.
// 
//*****************************************************************************
int Cmd_write(int argc, char *argv[])
{
	int count, letter;
	int args = 2;
	char file[7];
	char data[508];

	if(argc < 2)
	{
		UARTSend("This function requires a file name and text to be written\n\r");
		return -3;
	}

	for(count=0; argv[1][count]!=0; count++)
		file[count] = argv[1][count];
	for(; count<7; count++)
		file[count] = ' ';
	count=0;
	while(args < argc)
	{
		letter=0;
		for(; argv[args][letter]!=0 && count<508; letter++, count++)
			data[count] = argv[args][letter];
		++args;
		if(count!=508 && args!=argc)
			data[count] = ' ';
	}
	for(; count<508; count++)
		data[count] = 0;
	if(eFile_WOpen(file))
	{
		for(count = 0; count<508; count++){
			eFile_Write(data[count]);
		}
		return 0;
	}
	else
		UARTSend("Unable to write data\n\r");
	return -3;

}

//*****************************************************************************
//
// ORIGINAL FUNCTION WRITTEN BY PAUL KRETER
// ARGUMENT PARSING ADAPTED FROM CMDLINE EXAMPLE
//
// This function implements the "new" command.
// 
//*****************************************************************************
int Cmd_new(int argc, char *argv[])
{
	int count;
	char file[7];

	if(argc != 2)
	{
		UARTSend("This function requires a file name argument\n\r");
		return -3;
	}

	for(count = 0; argv[1][count] != 0; count++)
		file[count] = argv[1][count];
	for(; count<7; count++)
		file[count] = ' ';
	eFile_Create(file);
	return 0;
}

//*****************************************************************************
//
// ORIGINAL FUNCTION WRITTEN BY PAUL KRETER
// ARGUMENT PARSING ADAPTED FROM CMDLINE EXAMPLE
//
// This function implements the "tachometer" command.
// 
//*****************************************************************************
int Cmd_tach(int argc, char *argv[])
{
	unsigned long period = Tachometer_GetPeriod() * 125 / 1000;
	UARTSendLong(period);	
	UARTSend(" microsecond period\n\r");
	UARTSendLong(60 * 1000000 / (period*8));
	UARTSend(" RPMs\n\r");

	return 0;
}
